home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / LIBRARY / DSUTIL12 / OPTEXE / OPTEXE.PAS < prev   
Pascal/Delphi Source File  |  1993-09-20  |  20KB  |  599 lines

  1. {-----------------------------------------------------------------------}
  2. { PROJECT        NON-PROFIT HIGH QUALITY PROFESSIONAL SOFTWARE,  }
  3. {            AVAILABLE FOR ALL WORLD                }
  4. { LIBRARY        SYSTEM UTILITIES                                }
  5. { MODULE        OPTIMIZE_EXE_FILE                               }
  6. { FILE NAME        OPTEXE.PAS                    }
  7. { PURPOSE        Optimize the time execution for the DOS execu-  }
  8. {            table files.                    }
  9. { VERSION        1.10                        }
  10. { DATE            20-Sep-93                    }
  11. { DESIGN        Dmitry Stefankov                }
  12. { IMPLEMENTATION    Dmitry Stefankov                 }
  13. { COMPANY        Freelance Software Engineer            }
  14. { ADDRESS        Isakowskogo str, 4-2-30                }
  15. {            Moscow, 123181                    }
  16. {            USSR                        }
  17. {            Tel. 007 (095) 944-6304                }
  18. { COPYRIGHT NOTICE    Copyright (C) 1987-1993, Dmitry Stefankov    }
  19. { RESTRICTED RIGHTS    AVAILABLE ONLY FOR FREE DISTRIBUTION,           }
  20. {            NOT FOR COMMERCIAL PURPOSE            }
  21. { COMPUTER        IBM PC or compatible                }
  22. { OPERATING SYSTEM    MS/PC-DOS Version 3.30 or higher        }
  23. { COMPILER        Turbo Pascal Version 6.0            }
  24. {                       (Borland International Inc.) or compatible      }
  25. { ASSEMBLY LANGUAGE    Microsoft MASM 5.10 or compatible               }
  26. { LINKER        Turbo Pascal internal                           }
  27. { ARGUMENTS        <infile>   -  input  stream                     }
  28. {                       <outfile>  -  output stream                     }
  29. { RETURN        None                        }
  30. { REQUIRES        None                                            }
  31. { NATURAL LANGUAGE      English Language                                 }
  32. { SPECIAL        This program uses simple optimization method:   }
  33. {                       If image size < 64K then all intersegment calls }
  34. {                       are replaced to intrasegment calls.             }
  35. {                       The following table is displayed the time execu-}
  36. {                       tion taken from the standard INTEL data sheets: }
  37. {                             <Instr/Clocks/Processor>                  }
  38. {                                        8086 80186 80286 80386 80486   }
  39. {                            NOP          3     3     3     3     1     }
  40. {                            PUSH CS     10     9     3     2     3     }
  41. {                            NEAR CALL   19    15     7    7+m    3     }
  42. {                              TOTAL     32    27    13   12+m    7     }
  43. {                            FAR CALL    28    23    13   17+m    18+   }
  44. {                         optimization   bad    bad   none good   good  }
  45. {        WARNING!! Most files, compiled under Turbo Pascal,    !!    }
  46. {        WARNING!! Microsoft-C, Borland-C, etc may be not work !!    }
  47. {        WARNING!! after this optimization. Why? Don't ask me. !!    }
  48. { DESCRIPTION        1. Read input stream                            }
  49. {                       2. Search relocation and memory image to opti-  }
  50. {                       mize code                                       }
  51. {                       3. Write output stream                          }
  52. { REVISION HISTORY    Dima Stefankov (DS)                }
  53. {               1.00   02-Jul-93  DS  initilal release        }
  54. {            1.01   04-Jul-93  DS  fixed a bug with EXE-file    }
  55. {                          detection            }
  56. {            1.10   20-Sep-93  DS  some style updates    }
  57. {-----------------------------------------------------------------------}
  58.  
  59.  
  60. {*======================= PROGRAM HEADER PART ==========================*}
  61.  
  62. PROGRAM   OptimizeProgramTime;
  63.  
  64.  
  65. {*** other modules ***}
  66. {*USES;*}
  67.  
  68.  
  69. {** switches for compilation **}
  70. {$S-}                {*  stack checking        *}
  71. {$R-}                   {*  range checking        *}
  72. {$M 2048,0,0}           {*  stack/heapmin/heapmax *}
  73.  
  74.  
  75. {*========================== CONSTANTS PART ============================*}
  76.  
  77. CONST
  78.      asPurpose                  =       'EXE-File Optimizer';
  79.      asVersion                  =       '1.10';
  80.      asAuthor                   =       'Dima Stefankov';
  81.      asCopyright                =       'Copyright (c) 1987, 1993';
  82.      asProgram                  =       'OptExe';
  83.      asProgramPrompt            =       'OptExe'+': ';
  84.      asProgramU                 =       'OPTEXE';
  85.  
  86.      { exit codes }
  87.      errTerminateOK             =     0;
  88.      errBadParmsNumber          =     1;
  89.      errSourceNotFound          =     2;
  90.      errDestDontWrite           =     3;
  91.      errSrcOpenFailed           =     4;
  92.      errDestCreateFailed        =     5;
  93.      errSrcTooBig               =     6;
  94.      errSrcRead                 =     7;
  95.      errDestWrite               =     8;
  96.      errNotEnoughMemory         =     9;
  97.      errSrcNotValidEXE          =    10;
  98.      errEmptyRelocTable         =    11;
  99.      errBadRelocTableOfs        =    12;
  100.      errBadElementTableOfs      =    13;
  101.      errBadNearCallOfs          =    14;
  102.      errNoOptimizedElements     =    15;
  103.      errBadMemoryDealloc        =    16;
  104.  
  105.  
  106.      achNULL                    =     #0;
  107.  
  108.      achYes                     =     'Y';
  109.      achNo                      =     'N';
  110.  
  111.      achHexPrefix               =     '$';
  112.      achDosExtMark              =     '.';
  113.      asInDefExt                 =     'exe';
  114.      asOutDefExt                =     'exe';
  115.  
  116.      aHexRadix                  =     16;
  117.      aBytesPer64K               =     65536;
  118.      aBytesPerPara              =     16;
  119.      aParasPer64K               =     aBytesPer64K div aBytesPerPara;
  120.  
  121.      adwExeDosFileID_1          =     $4D5A;            { 'MZ' }
  122.      adwExeDosFileID_2          =     $5A4D;            { 'ZM' }
  123.      adwOptimizedFileID         =     $5344;            { 'DS' }
  124.  
  125.      adwExeFileID_Ofs           =     $00;
  126.      adwExeFileRelCount_Ofs     =     $06;
  127.      adwExeFileHeaderSize_Ofs   =     $08;
  128.      adwExeFileRelTable_Ofs     =     $18;
  129.      adwExeFileRelTableStart_Ofs=     $1C;
  130.      adwExeFileCheckSum_Ofs     =     $12;
  131.  
  132.  
  133.      adbFarCallOpcode           =     $9A;
  134.      adbNopOpcode               =     $90;
  135.      adbPushCSOpcode            =     $0E;
  136.      adbNearCallOpcode          =     $E8;
  137.  
  138.  
  139. {*====================== TYPED CONSTANTS PART ==========================*}
  140.  
  141. CONST
  142.  
  143.     setHexChars  :    SET OF System.Char  =  ['0'..'9','A'..'F','a'..'f'];
  144.  
  145.  
  146. {*=========================== VARIABLES PART ===========================*}
  147.  
  148. VAR
  149.  
  150.    gfInputStream    :   FILE;
  151.    gsInFileName     :   STRING[80];
  152.  
  153.    gfOutputStream   :   FILE;
  154.    gsOutFileName    :   STRING[80];
  155.  
  156.    gddByteCount     :   System.Longint;
  157.    gddTemp          :   System.Longint;
  158.  
  159.    gdwMemBlockSize  :   System.Word;
  160.    gdwBytesRead     :   System.Word;
  161.    gdwMemoryBlock   :   System.Word;
  162.    gdwOffsetInBuf   :   System.Word;
  163.    gdwRelEntries    :   System.Word;
  164.    gdwRelocTableOfs :   System.Word;
  165.    gdwImageStart    :   System.Word;
  166.    gdwElementOfs    :   System.Word;
  167.    gdwElementSeg    :   System.Word;
  168.    gdwImageOfs      :   System.Word;
  169.    gdwFarCallOfs    :   System.Word;
  170.    gdwFarCallSeg    :   System.Word;
  171.    gdwNewCallOfs    :   System.Word;
  172.    gdwTemp          :   System.Word;
  173.  
  174.    gdwOldRelCount   :   System.Word;
  175.    gdwNewRelCount   :   System.Word;
  176.    gdwNewRelTablOfs :   System.Word;
  177.    gdwFirstFreeOfs  :   System.Word;
  178.  
  179.    giErrorCode      :   System.Integer;
  180.  
  181.    gsTempInput      :   STRING;
  182.    gchInUser        :   System.Char;
  183.    gdbOpCode        :   System.Byte;
  184.  
  185.  
  186.  
  187. {*=========================== FUNCTIONAL PART ==========================*}
  188.  
  189. FUNCTION  _fnbFileExist(VAR fStruc : FILE; sFileName : STRING) : System.Boolean;
  190. {* Check that file exits. *}
  191. VAR
  192.   bResult  :  System.Boolean;
  193.  
  194. BEGIN
  195.   {** try to open the file **}
  196.   System.Assign(fStruc,sFileName);
  197.   {$I-}
  198.   System.Reset(fStruc);
  199.   {$I+}
  200.  
  201.   {** copy the result of last I/O operation **}
  202.   bResult := (System.IOResult = 0);
  203.  
  204.   IF (bResult)
  205.     THEN  System.Close(fStruc);
  206.   {if-then}
  207.  
  208.   _fnbFileExist := bResult;
  209. END; { _fnbFileExist }
  210.  
  211.  
  212. FUNCTION  _fnsForceFileNameExt(sFileName, sDefExt : STRING) : STRING;
  213. {* Add extension for filename if not present. *}
  214. BEGIN
  215.    IF (System.Pos(achDosExtMark,sFileName) = 0)
  216.      THEN  sFileName := sFileName + achDosExtMark + sDefExt;
  217.    {if-then}
  218.   _fnsForceFileNameExt := sFileName;
  219. END;
  220. { _fnsForceFileNameExt }
  221.  
  222.  
  223. FUNCTION  _fnsUpcaseStr(sInput : STRING) : STRING;
  224. {* Make all uppercase. *}
  225. VAR
  226.   dbIndex  :  System.BYTE;
  227.   dbCount  :  System.BYTE;
  228.  
  229. BEGIN
  230.   dbCount := System.Length(sInput);
  231.  
  232.   IF (dbCount <> 0)  THEN
  233.     FOR dbIndex :=  1  TO  dbCount DO
  234.       sInput[dbIndex] := System.Upcase(sInput[dbIndex]);
  235.     {for-to-do}
  236.   {if-then}
  237.  
  238.    _fnsUpcaseStr := sInput;
  239. END; { _fnsUpcaseStr }
  240.  
  241.  
  242.  
  243. FUNCTION   _fnchGetFirstChar(sInput : STRING) : System.Char;
  244. {* Returns a first char from string. *}
  245. VAR
  246.   chTemp  :  System.Char;
  247.  
  248. BEGIN
  249.    IF (System.Length(sInput) <> 0)
  250.      THEN  chTemp := sInput[1]
  251.      ELSE  chTemp := achNULL;
  252.    {if-then-else}
  253.   _fnchGetFirstChar := chTemp;
  254. END;
  255. { _fnchGetFirstChar }
  256.  
  257.  
  258. FUNCTION  _fndwGetByteFromBuf(dwBufOfs : System.Word) : System.Byte;
  259. {* Returns a byte from the file buffer. *}
  260. BEGIN
  261.    _fndwGetByteFromBuf := System.Mem[gdwMemoryBlock:dwBufOfs];
  262. END;
  263. { _fndwGetByteFromBuf }
  264.  
  265.  
  266.  
  267. FUNCTION  _fndwGetWordFromBuf(dwBufOfs : System.Word) : System.Word;
  268. {* Returns a word from the file buffer. *}
  269. BEGIN
  270.    _fndwGetWordFromBuf := System.MemW[gdwMemoryBlock:dwBufOfs];
  271. END;
  272. { _fndwGetWordFromBuf }
  273.  
  274.  
  275. {*=========================== PROCEDURAL PART ==========================*}
  276.  
  277. PROCEDURE    _CopyrightDisplay;
  278. {* Outputs the copyright notice. *}
  279. BEGIN
  280.      System.WriteLn(asPurpose+'  Version '+asVersion+',  '+asCopyright+'  '+asAuthor);
  281. END;  { _CopyrightDisplay }
  282.  
  283.  
  284. PROCEDURE _PutByteToBuf(dwBufOfs : System.Word; dbNewValue : System.Byte);
  285. {* Writes a byte to the file buffer. *}
  286. BEGIN
  287.    System.Mem[gdwMemoryBlock:dwBufOfs] := dbNewValue;
  288. END;
  289. { _PutByteToBuf }
  290.  
  291.  
  292. PROCEDURE  _PutWordToBuf(dwBufOfs, dbNewValue : System.Word);
  293. {* Writes a word to the file buffer. *}
  294. BEGIN
  295.    System.MemW[gdwMemoryBlock:dwBufOfs] := dbNewValue;
  296. END;
  297. { _PutWordToBuf }
  298.  
  299.  
  300.  
  301. PROCEDURE  _DosAllocMem(VAR dwDosMemSeg : System.Word; dwParas : System.Word);
  302. {* Allocates a memory block through the DOS service. *}
  303. VAR
  304.   bFuncFail  :   System.Boolean;
  305.  
  306. BEGIN
  307.   asm
  308.       mov       bFuncFail, System.False         { assume no fails   }
  309.       mov       bx, dwParas                     { # of bytes * 16   }
  310.       mov       ah, 48h                         { Dos func number   }
  311.       int       21h                             { call DOS service  }
  312.       jnc      @AllocDone                       { check for error   }
  313.       mov       bFuncFail, System.True          { Yes: err occurred }
  314.       jmp      @Done
  315.    @AllocDone:
  316.       les      di, dwDosMemSeg                  { access to TP var  }
  317.       mov      es:[di], ax                      { store a value     }
  318.    @Done:
  319.   end;
  320.   {asm-end}
  321.   IF  (bFuncFail)
  322.     THEN  BEGIN
  323.         System.WriteLn(asProgramPrompt+'Insufficient memory.');
  324.         System.Halt(errNotEnoughMemory);
  325.           END
  326.   {if-then}
  327. END;
  328. {  _DosAllocMem }
  329.  
  330.  
  331. PROCEDURE  _DosDeAllocMem(dwDosMemSeg : System.Word);
  332. {* Deallocates a memory block through the DOS service. *}
  333. VAR
  334.   bFuncFail  :   System.Boolean;
  335.  
  336. BEGIN
  337.   asm
  338.       mov       bFuncFail, System.False         { assume no fails   }
  339.       mov       es, dwDosMemSeg                 { seg of mem block  }
  340.       mov       ah, 49h                         { Dos func number   }
  341.       int       21h                             { call DOS service  }
  342.       jnc      @Done                            { check for error   }
  343.       mov       bFuncFail, System.True          { Yes: err occurred }
  344.    @Done:
  345.   end;
  346.   {asm-end}
  347.   IF  (bFuncFail)
  348.     THEN  BEGIN
  349.         System.WriteLn(asProgramPrompt+'Deallocation of memory failed.');
  350.         System.Halt(errBadMemoryDealloc);
  351.           END
  352.   {if-then}
  353. END;
  354. {  _DosDeAllocMem }
  355.  
  356.  
  357.  
  358. {*============================== MAIN PART =============================*}
  359.  
  360. BEGIN
  361.   _CopyrightDisplay;
  362.  
  363.      IF (System.ParamCount <> 2) THEN
  364.      BEGIN
  365.           System.WriteLn(asProgramPrompt+'  screen help for you.');
  366.           System.WriteLn('Usage: infile outfile');
  367.           System.WriteLn('  infile   -  source filename      (def. ext. = '+asInDefExt+')');
  368.           System.WriteLn('  outfile  -  destination filename (def. ext. = '+asOutDefExt+')');
  369.           System.Halt(errBadParmsNumber);
  370.      END; { if }
  371.  
  372.  
  373.   {** copy the parameters from command line **}
  374.   gsInFileName := _fnsForceFileNameExt(System.ParamStr(1),asInDefExt);
  375.   gsInFileName  := _fnsUpcaseStr(gsInFileName);
  376.  
  377.   gsOutFileName := _fnsForceFileNameExt(System.ParamStr(2),asOutDefExt);
  378.   gsOutFileName := _fnsUpcaseStr(gsOutFileName);
  379.  
  380.  
  381.   {** source file exists? **}
  382.   IF  NOT(_fnbFileExist(gfInputStream,gsInFileName)) THEN
  383.   BEGIN
  384.     System.WriteLn(asProgramPrompt+'Unable to open file '+gsInFileName);
  385.     System.Halt(errSourceNotFound);
  386.   END;
  387.   {if-then}
  388.  
  389.  
  390.   {** destination file present? **}
  391.   IF (_fnbFileExist(gfOutputStream,gsOutFileName)) THEN
  392.   BEGIN
  393.     System.Write(asProgramPrompt+'Output file '+gsOutFileName+
  394.                  ' already exists. Overwrite? (n/y): ');
  395.     System.ReadLn(gsTempInput);
  396.     IF (System.UpCase(_fnchGetFirstChar(gsTempInput)) <> achYes)
  397.       THEN  System.Halt(errDestDontWrite);
  398.     {if-then}
  399.   END;
  400.   {if-then}
  401.  
  402.  
  403.   {** open the source file **}
  404.   System.Assign(gfInputStream,gsInFileName);
  405.   {$I-}
  406.   System.Reset(gfInputStream,1);
  407.   {$I+}
  408.  
  409.   IF  (System.IoResult <> 0) THEN
  410.   BEGIN
  411.     System.WriteLn(asProgramPrompt+'Unable to open '+gsInFileName);
  412.     System.Halt(errSrcOpenFailed);
  413.   END;
  414.   {if-then}
  415.  
  416.  
  417.  {** check for size < 64K }
  418.   gddByteCount := System.FileSize(gfInputStream);
  419.   IF (gddByteCount > aBytesPer64K)
  420.     THEN BEGIN
  421.       System.WriteLn(asProgramPrompt+'Input file size more than 64K.');
  422.       System.Halt(errSrcTooBig);
  423.          END;
  424.   {if-then}
  425.  
  426.  
  427.   {** get memory from DOS **}
  428.   _DosAllocMem(gdwMemoryBlock,aParasPer64K);
  429.  
  430.  
  431.   {** transfer input file to memory buffer (all file!) **}
  432.    System.WriteLn(asProgramPrompt+'Source reading...');
  433.    System. BlockRead(gfInputStream,
  434.                     System.Mem[gdwMemoryBlock:0],
  435.                     gddByteCount,
  436.                     gdwBytesRead);
  437.    IF (gddByteCount <> gdwBytesRead)
  438.      THEN  BEGIN
  439.          System.WriteLn(asProgramPrompt+'Reading error.');
  440.          System.Halt(errSrcRead);
  441.            END;
  442.    {if-then}
  443.  
  444.  
  445.   {** close input file **}
  446.   System.Close(gfInputStream);
  447.  
  448.  
  449.   {** check for executable file **}
  450.    gdwOffsetInBuf := adwExeFileID_Ofs;
  451.    gdwTemp := _fndwGetWordFromBuf(gdwOffsetInBuf);
  452.    IF  (NOT(gdwTemp = adwExeDosFileID_1) AND 
  453.        NOT(gdwTemp = adwExeDosFileID_2))
  454.      THEN  BEGIN
  455.          System.WriteLn(asProgramPrompt+'Not valid EXE-image.');
  456.          System.Halt(errSrcNotValidEXE);
  457.            END;
  458.    {if-then}
  459.  
  460.  
  461.   {** check for presence of relocation table **}
  462.    gdwOffsetInBuf := adwExeFileRelCount_Ofs;
  463.    gdwRelEntries := _fndwGetWordFromBuf(gdwOffsetInBuf);
  464.    IF  (gdwRelEntries = 0)
  465.      THEN  BEGIN
  466.          System.WriteLn(asProgramPrompt+'Empty relocation table.');
  467.          System.Halt(errEmptyRelocTable);
  468.            END;
  469.    {if-then}
  470.  
  471.  
  472.   {** check for presence of relocation table **}
  473.    gdwOffsetInBuf := adwExeFileRelTable_Ofs;
  474.    gdwRelocTableOfs := _fndwGetWordFromBuf(gdwOffsetInBuf);
  475.    IF  (gdwRelocTableOfs < adwExeFileRelTableStart_Ofs)
  476.      THEN  BEGIN
  477.          System.WriteLn(asProgramPrompt+'Invalid relocation table offset.');
  478.          System.Halt(errBadRelocTableOfs);
  479.            END;
  480.    {if-then}
  481.  
  482.   
  483.   {** main optimization loop **}
  484.     gdwOffsetInBuf := adwExeFileHeaderSize_Ofs;
  485.     gdwImageStart := _fndwGetWordFromBuf(gdwOffsetInBuf) * aBytesPerPara;
  486.     gdwNewRelCount   := 0;
  487.     gdwNewRelTablOfs := gdwRelocTableOfs;
  488.     gdwFirstFreeOfs := gdwNewRelTablOfs;
  489.     gdwOldRelCount := gdwRelEntries;
  490.  
  491.     System.WriteLn(asProgramPrompt+'Search relocation table to optimize.');
  492.     WHILE (gdwRelEntries <> 0)  DO
  493.     BEGIN
  494.          gdwOffsetInBuf := gdwRelocTableOfs;
  495.          gdwElementOfs := _fndwGetWordFromBuf(gdwOffsetInBuf);
  496.          gdwElementSeg := _fndwGetWordFromBuf(gdwOffsetInBuf+2);
  497.  
  498.          gddTemp := (System.Longint(gdwElementSeg) * aBytesPerPara) + gdwElementOfs;
  499.          IF  (gddTemp >= aBytesPer64K)
  500.            THEN  BEGIN
  501.               System.WriteLn(asProgramPrompt+'Relocation element offset more than 64K.');
  502.               System.Halt(errBadElementTableOfs);
  503.                  END;
  504.          {if-then}
  505.  
  506.          gdwImageOfs := System.Word(gddTemp)+gdwImageStart;
  507.          gdwTemp := gdwImageOfs - 3;
  508.          gdbOpCode := _fndwGetWordFromBuf(gdwTemp+0);
  509.          gdwFarCallOfs := _fndwGetWordFromBuf(gdwTemp+1);
  510.          gdwFarCallSeg := _fndwGetWordFromBuf(gdwTemp+3);
  511.  
  512.          IF  (gdbOpCode = adbFarCallOpcode)
  513.            THEN  BEGIN
  514.                gddTemp := (System.Longint(gdwFarCallSeg) * aBytesPerPara) + gdwFarCallOfs;
  515.                IF  (gddTemp >= aBytesPer64K)
  516.                  THEN  BEGIN
  517.                     System.WriteLn(asProgramPrompt+'Offset for NEAR CALL not within 64K.');
  518.                     System.Halt(errBadNearCallOfs);
  519.                        END;
  520.                {if-then}
  521.                gdwNewCallOfs := System.Word(gddTemp) - (gdwImageOfs + 2) + gdwImageStart;
  522.                _PutByteToBuf(gdwTemp+0,adbNopOpcode);
  523.                _PutByteToBuf(gdwTemp+1,adbPushCSOpcode);
  524.                _PutByteToBuf(gdwTemp+2,adbNearCallOpcode);
  525.                _PutWordToBuf(gdwTemp+3,gdwNewCallOfs);
  526.                _PutWordToBuf(gdwOffsetInBuf+0,$0);      { wipe entry }
  527.                _PutWordToBuf(gdwOffsetInBuf+2,$0);
  528.                  END
  529.            ELSE  BEGIN
  530.                _PutWordToBuf(gdwFirstFreeOfs+0,gdwElementOfs);
  531.                _PutWordToBuf(gdwFirstFreeOfs+2,gdwElementSeg);
  532.                System.Inc(gdwNewRelCount);
  533.                System.Inc(gdwFirstFreeOfs,4);
  534.                  END;
  535.          {if-then-else}
  536.  
  537.          System.Dec(gdwRelEntries);
  538.          System.Inc(gdwRelocTableOfs,4)
  539.     END;
  540.     {while-do}
  541.  
  542.  
  543.   {** check if optimization had occurred **}
  544.     IF (gdwOldRelCount = gdwNewRelCount)
  545.       THEN BEGIN
  546.          System.WriteLn(asProgramPrompt+'No elements to optimize.');
  547.          System.Halt(errNoOptimizedElements);
  548.            END
  549.       ELSE BEGIN
  550.          _PutWordToBuf(adwExeFileRelCount_Ofs,gdwNewRelCount);
  551.          _PutWordToBuf(adwExeFileCheckSum_Ofs,adwOptimizedFileID);
  552.           System.WriteLn(asProgramPrompt+'Total relocation table entries = ',gdwOldRelCount);
  553.           System.WriteLn(asProgramPrompt+'Opt.  relocation table entries = ',gdwOldRelCount-gdwNewRelCount);
  554.            END;
  555.     {if-then-else}
  556.  
  557.  
  558.   {** create the destination file **}
  559.   System.Assign(gfOutputStream,gsOutFileName);
  560.   {$I-}
  561.   System.Rewrite(gfOutputStream,1);
  562.   {$I+}
  563.   IF  (System.IoResult <> 0) THEN
  564.   BEGIN
  565.     System.WriteLn(asProgramPrompt+'Unable to create '+gsOutFileName);
  566.     System.Halt(errDestCreateFailed);
  567.   END;
  568.   {if-then}
  569.  
  570.  
  571.   {** copy modified source to destination **}
  572.    System.WriteLn(asProgramPrompt+'Target writing...');
  573.    System.BlockWrite(gfOutputStream,
  574.                     System.Mem[gdwMemoryBlock:0],
  575.                      gddByteCount,
  576.                      gdwBytesRead);
  577.    IF (gddByteCount <> gdwBytesRead)
  578.      THEN  BEGIN
  579.          System.WriteLn(asProgramPrompt+'Reading error.');
  580.          System.Halt(errDestWrite);
  581.            END;
  582.    {if-then}
  583.  
  584.  
  585.   {** free memory back to DOS **}
  586.   _DosDeAllocMem(gdwMemoryBlock);
  587.  
  588.  
  589.   {** close output file **}
  590.   System.Close(gfOutputStream);
  591.  
  592.  
  593.   {** report all done **}
  594.   System.WriteLn(asProgramPrompt+'Done.');
  595.  
  596.   {* System.Halt(errTerminateOk); *}
  597. END.
  598.  
  599.